package com.apobates.forum.toro.utils;

import com.expediagroup.beans.BeanUtils;
import com.expediagroup.transformer.utils.ClassUtils;
import com.expediagroup.transformer.utils.ReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.text.Collator;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

public class BullBeanHelper {
    private final static Logger logger = LoggerFactory.getLogger(BullBeanHelper.class);
    private final static BeanUtils buIns;
    private final static ClassUtils cuIns;
    private final static ReflectionUtils ruIns;
    // Primitive Data Types
    // ://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
    // ://docs.oracle.com/javase/tutorial/java/generics/index.html
    private final static Map<String,Class<?>> PrimitiveTypeMap = new HashMap<>(9);
    static {
        buIns = new BeanUtils();
        cuIns = new ClassUtils();
        ruIns = new ReflectionUtils();
        // int、short、long、float、double、char、byte、boolen和void
        PrimitiveTypeMap.put("int", int.class);
        PrimitiveTypeMap.put("long", long.class);
        PrimitiveTypeMap.put("boolean", boolean.class);
        PrimitiveTypeMap.put("short", short.class);
        PrimitiveTypeMap.put("float", float.class);
        PrimitiveTypeMap.put("double", double.class);
        PrimitiveTypeMap.put("char", char.class);
        PrimitiveTypeMap.put("byte", byte.class);
        PrimitiveTypeMap.put("void", void.class);
    }

    /**
     * 使用Bull的BeanUtils来复制对象的属性
     * @param dest 设置的目标对象
     * @param orig 源目标对象
     */
    public static void copyProperties(Object dest, Object orig) {
        buIns.getTransformer().setDefaultValueForMissingField(true).transform(orig, dest.getClass());
    }

    /**
     * 构建指定类型的实例并使用Bull的BeanUtils将目标对象的属性复制到目标对象对象中
     * @param proxyObjClass 目标对象类型
     * @param selectObj 被复制的对象实例
     * @return 构建失败或属性值复制失败返回null
     * @param <T>
     * @param <U>
     */
    public static<T,U> T buildProxyInstance(Class<T> proxyObjClass,  U selectObj) {
        return buIns.getTransformer().setDefaultValueForMissingField(true).transform(selectObj, proxyObjClass);
    }

    /**
     * 使用Bull的BeanUtils克隆对象并进行参考对象的复制
     * @param cloneObj 被克隆的的对象
     * @param dest 使用指定对象来填充被克隆的对象
     * @return
     * @param <T>
     * @param <U>
     */
    public static <T,U> T cloneAndSetAttr(T cloneObj, U dest) {
        if (null==dest){
            throw new NullPointerException("复制对像不可用");
        }
        if (null==cloneObj){
            throw new NullPointerException("克隆对像不可用");
        }
        buIns.getTransformer().setDefaultValueForMissingField(true).transform(dest, cloneObj);
        return cloneObj;
    }

    /**
     * 构建指定Class的实例
     * @param ObjClass 目标对象类型
     * @return
     * @param <T>
     */
    public static <T> T buildInstance(Class<T> ObjClass) {
        T targetObj = null;
        try{
            targetObj = ObjClass.getDeclaredConstructor().newInstance();
        }catch (Exception e){
            logger.debug("[BH]build object instance has error:"+e.getMessage());
        }
        return targetObj;
    }

    /**
     * 生成指定对象的新实例.要求目标对象存在空参的构造函数
     * @param obj 目标对象实例
     * @return
     * @param <T>
     */
    public static <T> Object getNewInstance(Object obj) {
        Object val = null;
        try {
            val = cuIns.getInstance(obj.getClass().getDeclaredConstructor());
        }catch (Exception e){
            logger.debug("[BH]get object new instance has error:"+e.getMessage());
        }
        return val;
    }

    /**
     * 使用Bull的ClassUtils获取指定类的属性名称
     * @param targetClass 目标类型
     * @return
     * @param <T>
     */
    public static <T> Set<String> getClassField(Class<T> targetClass) {
        return cuIns.getDeclaredFields(targetClass, false).stream().map(Field::getName).collect(Collectors.toSet());
    }

    /**
     * 使用Bull的ReflectionUtils来获取指定对象的属性值
     * @param objIns 对象实例
     * @param attrName 获取的属性名称
     * @return
     * @param <T>
     */
    public static <T> Object getAttrVal(T objIns, String attrName) {
        return ruIns.getFieldValue(objIns, ruIns.getDeclaredField(attrName, objIns.getClass()));
    }

    /**
     * 查看指定类型的默认值
     * @param type 类型
     * @return
     */
    public static Object getAttrDefaultVal(java.lang.Class<?> type) {
        return cuIns.getDefaultTypeValue(type);
    }

    /**
     * 查看指定类型的默认值
     * @param typeName 类型名称;基础类型使用名称(long).包装类和字符串使用类全名(java.lang.Long)
     * @return
     */
    public static Object getAttrDefaultVal(String typeName) {
        Class<?> fieldType = null;
        try {
            if (PrimitiveTypeMap.containsKey(typeName.toLowerCase())){
                fieldType = PrimitiveTypeMap.get(typeName.toLowerCase());
            } else {
                fieldType = Class.forName(typeName);
            }
        }catch (ClassNotFoundException e){
            logger.debug("[BH]get argument Class type has error:"+e.getMessage());
        }
        if (null != fieldType){
            return getAttrDefaultVal(fieldType);
        }
        return null;
    }

    /**
     * 判断指定的类型是否是原始数据类型(Primitive Data Types)
     * @param clazz
     * @return
     */
    public static boolean isPrimitive(java.lang.Class<?> clazz) {
        return cuIns.isPrimitiveType(clazz);
    }

    /**
     * 获取指定类型名称的Class
     * @param typename 类型名称.基础类型使用名称(long).包装类和字符串使用类全名(java.lang.Long)
     * @return
     * @throws IllegalAccessException
     */
    public static Class<?> getTypeClass(String typename) throws IllegalAccessException {
        Class<?> fieldType = null;
        try {
            if (PrimitiveTypeMap.containsKey(typename.toLowerCase())){
                fieldType = PrimitiveTypeMap.get(typename.toLowerCase());
            } else {
                fieldType = Class.forName(typename);
            }
        }catch (ClassNotFoundException e){
            throw new IllegalAccessException("未知的属性类型");
        }
        return fieldType;
    }

    /**
     * 使用Bull的ReflectionUtils获取指定对象的属性值
     * @param objIns 对象实例
     * @param attrName 获取的属性名称
     * @param attrTypeName 属性的类型名称.基础类型使用名称(long).包装类和字符串使用类全名(java.lang.Long)
     * @return
     * @param <T>
     * @throws IllegalAccessException
     */
    public static <T> Object getAttrVal(T objIns, String attrName, String attrTypeName) throws IllegalAccessException {
        Class<?> fieldType = getTypeClass(attrTypeName);
        if (null != fieldType){
            return getAttrVal(objIns, attrName, fieldType);
        }
        return null;
    }

    /**
     * 获取指定对象的属性
     * @param objIns 对象
     * @param attrName 属性名称
     * @param attrTypeName 属性类型
     * @return
     * @param <T>
     */
    public static <T> Object getAttrVal(T objIns, String attrName, Class<?> attrTypeName) {
        return ruIns.getFieldValue(objIns, attrName, attrTypeName);
    }

    /**
     * 使用Bull的ReflectionUtils获取指定对象的多个属性值
     * @param objIns 对象实例
     * @param attrNamesTypeMap 获取的属性名称和值类型名称集合,key=属性名称,value=属性的类型名称
     * @return key=属性名称,values=属性值
     * @param <T>
     */
    public static <T> Map<String,Object> getMultiAttrVal(T objIns, Map<String,String> attrNamesTypeMap) {
        if(null == objIns || attrNamesTypeMap.isEmpty()){
            return Collections.emptyMap();
        }
        Function<Map.Entry<String,String>,String> keyFun = (entry)->entry.getKey();
        Function<Map.Entry<String,String>,Object> valFun = (entry)->{
            try {
                return getAttrVal(objIns, entry.getKey(), entry.getValue());
            }catch (Exception e){
                return getAttrDefaultVal(entry.getValue());
            }
        };
        return attrNamesTypeMap.entrySet().stream().collect(Collectors.toMap(keyFun, valFun));
    }

    /**
     * 设置指定对象的多个属性值
     * @param objIns 对象实例
     * @param attrValMap key=属性名称,value=属性的值
     * @return 返回设置成功的数量
     * @param <T>
     */
    public static <T> int setMultiAttrVal(T objIns, Map<String,Object> attrValMap) {
        int affect = 0;
        // 获取的属性名集集合
        Set<String> keySet = attrValMap.keySet();
        for(String fieldName : keySet){
            setAttrVal(objIns, fieldName, attrValMap.get(fieldName));
            affect+=1;
        }
        return affect;
    }

    /**
     * 设置对象的属性
     * @param objIns 目标对象
     * @param attrName 设置的对象属性名称
     * @param attrVal 设置的属性值
     * @param <T>
     */
    public static <T> void setAttrVal(T objIns, String attrName, Object attrVal) {
        ruIns.setFieldValue(objIns, attrName, attrVal);
    }

    /**
     * 对比两个对象实现是否相等.支持String,Comparable接口实现类
     * @param val 对象1
     * @param val2 对象2
     * @return 若任一参数不可能返回false,若参数非String,Comparable返回false,反之返回比较结果
     */
    public static boolean isSameObj(Object val, Object val2) {
        if(null == val || null == val2) {
            return false;
        }
        // 字符串
        if (val instanceof String && val2 instanceof String){
            // System.out.println("[BH]Object::String compare ");
            return Collator.getInstance().compare(val, val2) == 0;
        }
        // Comparable
        if(val instanceof Comparable && val2 instanceof Comparable){
            // System.out.println("[BH]Object::Comparable compare ");
            return ((Comparable) val).compareTo(val2) == 0;
        }
        // Equals
        return val.equals(val2);
    }
}
